// AP(r) Computer Science Marine Biology Simulation: // The BoundedEnv class is copyright(c) 2002 College Entrance // Examination Board (www.collegeboard.com). // // This class is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation. // // This class is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. /** * AP® Computer Science Marine Biology Simulation:<br> * The <code>BoundedEnv</code> class models a bounded, two-dimensional, * grid-like environment containing locatable objects. For example, * it could be an environment of fish for a marine biology simulation. * * <p> * The <code>BoundedEnv</code> class is * copyright© 2002 College Entrance Examination Board * (www.collegeboard.com). * * @author Alyce Brady * @author APCS Development Committee * @version 1 July 2002 * @see Locatable * @see Location **/ public class BoundedEnv extends SquareEnvironment { // Instance Variables: Encapsulated data for each BoundedEnv object private Locatable[][] theGrid; // grid representing the environment private int objectCount; // # of objects in current environment // constructors /** Constructs an empty BoundedEnv object with the given dimensions. * (Precondition: <code>rows > 0</code> and <code>cols > 0</code>.) * @param rows number of rows in BoundedEnv * @param cols number of columns in BoundedEnv **/ public BoundedEnv(int rows, int cols) { // Construct and initialize inherited attributes. super(); theGrid = new Locatable[rows][cols]; objectCount = 0; } // accessor methods /** Returns number of rows in the environment. * @return the number of rows, or -1 if this environment is unbounded **/ public int numRows() { return theGrid.length; } /** Returns number of columns in the environment. * @return the number of columns, or -1 if this environment is unbounded **/ public int numCols() { // Note: according to the constructor precondition, numRows() > 0, so // theGrid[0] is non-null. return theGrid[0].length; } /** Verifies whether a location is valid in this environment. * @param loc location to check * @return <code>true</code> if <code>loc</code> is valid; * <code>false</code> otherwise **/ public boolean isValid(Location loc) { if ( loc == null ) return false; return (0 <= loc.row() && loc.row() < numRows()) && (0 <= loc.col() && loc.col() < numCols()); } /** Returns the number of objects in this environment. * @return the number of objects **/ public int numObjects() { return objectCount; } /** Returns all the objects in this environment. * @return an array of all the environment objects **/ public Locatable[] allObjects() { Locatable[] theObjects = new Locatable[numObjects()]; int tempObjectCount = 0; // Look at all grid locations. for ( int r = 0; r < numRows(); r++ ) { for ( int c = 0; c < numCols(); c++ ) { // If there's an object at this location, put it in the array. Locatable obj = theGrid[r][c]; if ( obj != null ) { theObjects[tempObjectCount] = obj; tempObjectCount++; } } } return theObjects; } /** Determines whether a specific location in this environment is * empty. * @param loc the location to test * @return <code>true</code> if <code>loc</code> is a * valid location in the context of this environment * and is empty; <code>false</code> otherwise **/ public boolean isEmpty(Location loc) { return isValid(loc) && objectAt(loc) == null; } /** Returns the object at a specific location in this environment. * @param loc the location in which to look * @return the object at location <code>loc</code>; * <code>null</code> if <code>loc</code> is not * in the environment or is empty **/ public Locatable objectAt(Location loc) { if ( ! isValid(loc) ) return null; return theGrid[loc.row()][loc.col()]; } /** Creates a single string representing all the objects in this * environment (not necessarily in any particular order). * @return a string indicating all the objects in this environment **/ public String toString() { Locatable[] theObjects = allObjects(); String s = "Environment contains " + numObjects() + " objects: "; for ( int index = 0; index < theObjects.length; index++ ) s += theObjects[index].toString() + " "; return s; } // modifier methods /** Adds a new object to this environment at the location it specifies. * (Precondition: <code>obj.location()</code> is a valid empty location.) * @param obj the new object to be added * @throws IllegalArgumentException if the precondition is not met **/ public void add(Locatable obj) { // Check precondition. Location should be empty. Location loc = obj.location(); if ( ! isEmpty(loc) ) throw new IllegalArgumentException("Location " + loc + " is not a valid empty location"); // Add object to the environment. theGrid[loc.row()][loc.col()] = obj; objectCount++; } /** Removes the object from this environment. * (Precondition: <code>obj</code> is in this environment.) * @param obj the object to be removed * @throws IllegalArgumentException if the precondition is not met **/ public void remove(Locatable obj) { // Make sure that the object is there to remove. Location loc = obj.location(); if ( objectAt(loc) != obj ) throw new IllegalArgumentException("Cannot remove " + obj + "; not there"); // Remove the object from the grid. theGrid[loc.row()][loc.col()] = null; objectCount--; } /** Updates this environment to reflect the fact that an object moved. * (Precondition: <code>obj.location()</code> is a valid location * and there is no other object there. * Postcondition: <code>obj</code> is at the appropriate location * (<code>obj.location()</code>), and either <code>oldLoc</code> is * equal to <code>obj.location()</code> (there was no movement) or * <code>oldLoc</code> is empty.) * @param obj the object that moved * @param oldLoc the previous location of <code>obj</code> * @throws IllegalArgumentException if the precondition is not met **/ public void recordMove(Locatable obj, Location oldLoc) { // Simplest case: There was no movement. Location newLoc = obj.location(); if ( newLoc.equals(oldLoc) ) return; // Otherwise, oldLoc should contain the object that is // moving and the new location should be empty. Locatable foundObject = objectAt(oldLoc); if ( ! (foundObject == obj && isEmpty(newLoc)) ) throw new IllegalArgumentException("Precondition violation moving " + obj + " from " + oldLoc); // Move the object to the proper location in the grid. theGrid[newLoc.row()][newLoc.col()] = obj; theGrid[oldLoc.row()][oldLoc.col()] = null; } }